package com.zzh.zzhaiagent.service.impl;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzh.zzhaiagent.domain.dto.UserEmailAuthDTO;
import com.zzh.zzhaiagent.domain.dto.UserProfilesLoginDTO;
import com.zzh.zzhaiagent.domain.dto.UserProfilesUpdateDTO;
import com.zzh.zzhaiagent.domain.entity.UserProfiles;
import com.zzh.zzhaiagent.domain.vo.UserLoginTokenVO;
import com.zzh.zzhaiagent.domain.vo.UserProfilesVO;
import com.zzh.zzhaiagent.exception.BusinessException;
import com.zzh.zzhaiagent.mapper.UserProfilesMapper;
import com.zzh.zzhaiagent.service.EmailService;
import com.zzh.zzhaiagent.service.IUserProfilesService;
import com.zzh.zzhaiagent.utils.*;
import com.zzh.zzhaiagent.websocket.domain.dto.WxInfo;
import com.zzh.zzhaiagent.websocket.service.WebSocketService;
import com.zzh.zzhaiagent.websocket.service.impl.WebSocketServiceImpl;
import io.jsonwebtoken.Claims;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

import static com.zzh.zzhaiagent.constant.RedisKeyConstant.*;
import static com.zzh.zzhaiagent.websocket.service.impl.WebSocketServiceImpl.getWxInfoByUUID;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author zzh
 * @since 2025-05-07
 */
@Service
public class UserProfilesServiceImpl extends ServiceImpl<UserProfilesMapper, UserProfiles> implements IUserProfilesService {

    @Resource
    private EmailService emailService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private RedisHutoolHashUtil redisHutoolHashUtil;//存储用户信息到redis中Hash形式


    /**
     * 登录 邮箱 + code 登录
     * @param userProfilesLoginDTO 登录参数
     * @return 登录成功返回用户信息
     */
    @Override
    public UserLoginTokenVO loginEmail(UserProfilesLoginDTO userProfilesLoginDTO) {
        if (userProfilesLoginDTO == null){
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }

        String email = userProfilesLoginDTO.getEmail();
        String code = userProfilesLoginDTO.getCode();

        //校验参数
        if (StrUtil.isBlank(email) || StrUtil.isBlank(code)){
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }

        //邮箱是否正确
        if(!EmailRightUtils.isRightEmail(email)){
            throw new BusinessException(ResultCodeEnum.EMAIL_NOT_EXIST);
        }

        //code 要为 6 位
        if (code.length()!= 6) {
            throw new BusinessException(ResultCodeEnum.CODE_ERROR);
        }

        String rsCode = stringRedisTemplate.opsForValue().get(EMAIL_CODE_KEY_PREFIX + email);
        if (!code.equals(rsCode)){
            throw new BusinessException(ResultCodeEnum.CODE_ERROR);
        }

        //根据邮箱查询用户信息
        UserProfiles user = this.lambdaQuery().select(UserProfiles::getEmail, UserProfiles::getId,UserProfiles::getUserAccount,UserProfiles::getStatus).eq(UserProfiles::getEmail, email).one();
        //可能第一次登录，需要为其创建账户，并让其设置密码

        if (user == null || StrUtil.isBlank(user.getEmail()) || StrUtil.isBlank(user.getUserAccount())){
            //创建用户
            //插入数据库
            UserProfiles userProfiles = new UserProfiles();
            userProfiles.setEmail(email);
            userProfiles.setStatus(0);
            String account = UserAccountUtils.generateUserAccount(email);
            userProfiles.setUserAccount(account);
            this.baseMapper.insert(userProfiles);
            //生成accessToken 和 refreshToken
            String accessToken = JwtUtil.createJWT(String.valueOf(userProfiles.getId()));
            String refreshToken = JwtUtil.createRefreshToken(String.valueOf(userProfiles.getId()));
            //生成一个UUID作为refreshToken的key，存储到redis中
            String uuid = UUID.randomUUID().toString(false);//不携带-
            //存储refreshToken到redis (不应该用userId作为key，就用accessToken作为key)
            stringRedisTemplate.opsForValue().set(REFRESH_TOKEN_KEY_PREFIX + uuid,refreshToken,7, TimeUnit.DAYS);
            return UserLoginTokenVO.buildAccount(account,accessToken,uuid);
        }

        //生成accessToken 和 refreshToken
        String accessToken = JwtUtil.createJWT(String.valueOf(user.getId()));
        String refreshToken = JwtUtil.createRefreshToken(String.valueOf(user.getId()));
        //生成一个UUID作为refreshToken的key，存储到redis中
        String uuid = UUID.randomUUID().toString(false);//不携带-
        //存储refreshToken到redis (不应该用userId作为key，就用accessToken作为key)
        stringRedisTemplate.opsForValue().set(REFRESH_TOKEN_KEY_PREFIX + uuid,refreshToken,7, TimeUnit.DAYS);
        //如果查到用户但是没认证，也就是没有自定义密码的话，需要返回一个状态并且返回特定信息
        if (0 == user.getStatus()){
            return UserLoginTokenVO.buildAccount(user.getUserAccount(),accessToken,uuid);
        }
        //登录成功
        return UserLoginTokenVO.build(accessToken,uuid);
    }

    @Override
    public UserLoginTokenVO loginPhone(UserProfilesLoginDTO userProfilesLoginDTO) {
        return null;
    }

    @Override
    public UserLoginTokenVO loginPassword(UserProfilesLoginDTO userProfilesLoginDTO) {
        //账户密码登录也需要图片的验证码！(但是这个是前端来实现校验，后端不需要进行校验验证码)
        if (userProfilesLoginDTO == null){
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        String userAccount = userProfilesLoginDTO.getUserAccount();
        String password = userProfilesLoginDTO.getPassword();
        if (StrUtil.isBlank(userAccount) || StrUtil.isBlank(password)){
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        //根据账户查询用户信息
        UserProfiles user = this.lambdaQuery().select(UserProfiles::getPassword,UserProfiles::getSalt, UserProfiles::getId).eq(UserProfiles::getUserAccount, userAccount).one();
        if (user == null){
            throw new BusinessException(ResultCodeEnum.ACCOUNT_NOT_EXIST);
        }
        //password进行加密操作
        String salt = user.getSalt();
        String dbPassword = user.getPassword();
        //加密密码
        String encrypt = PasswordUtil.encrypt(password, salt);
        if (!encrypt.equals(dbPassword)){
            throw new BusinessException(ResultCodeEnum.PASSWORD_ERROR);
        }
        //密码正确返回token
        //生成accessToken 和 refreshToken
        String accessToken = JwtUtil.createJWT(String.valueOf(user.getId()));
        String refreshToken = JwtUtil.createRefreshToken(String.valueOf(user.getId()));
        //生成一个UUID作为refreshToken的key，存储到redis中
        String uuid = UUID.randomUUID().toString(false);//不携带-
        //存储refreshToken到redis
        stringRedisTemplate.opsForValue().set(REFRESH_TOKEN_KEY_PREFIX + uuid,refreshToken,7, TimeUnit.DAYS);
        return UserLoginTokenVO.build(accessToken,uuid);
    }

    @Override
    public UserProfilesVO getUserInfo(HttpServletRequest request) {
        //从token中获取用户id
        String token = RequestHeaderUtils.getAuthorization(request);
        if (StrUtil.isBlank(token)){
            throw new BusinessException(ResultCodeEnum.LOGIN_AUTH);//提示重新登录
        }
        Claims claims = JwtUtil.parseJWT(token);
        if (claims == null){
            throw new BusinessException(ResultCodeEnum.LOGIN_AUTH);//提示重新登录
        }
        String userId = claims.getSubject();
        if (StrUtil.isBlank(userId)){
            throw new BusinessException(ResultCodeEnum.LOGIN_AUTH);//提示重新登录
        }
        //从缓存中获取用户信息
        UserProfilesVO userProfilesVO = redisHutoolHashUtil.getUserProfiles(USER_INFO_KEY_PREFIX + userId);
        if (userProfilesVO != null){
            return userProfilesVO;
        }
        //缓存没有，从数据库中获取用户信息
        //根据id查询用户信息
        UserProfiles userProfiles = this.baseMapper.selectById(userId);
        if (userProfiles == null){
            throw new BusinessException(ResultCodeEnum.LOGIN_AUTH);//提示重新登录
        }
        //存储到redis中
        userProfilesVO = UserProfilesVO.builderDBToVO(userProfiles);
        redisHutoolHashUtil.setUserProfiles(USER_INFO_KEY_PREFIX + userId,userProfilesVO,2);//2小时过期

        //返回用户信息
        return userProfilesVO;
    }


    //修改密码 (通过账户来修改密码)
    @Override
    public String updatePassword(String password, HttpServletRequest request) {
        //获取token中的用户信息
        if (StrUtil.isBlank(password)){
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        String token = RequestHeaderUtils.getAuthorization(request);
        if (StrUtil.isBlank(token)){
            throw new BusinessException(ResultCodeEnum.LOGIN_AUTH);//提示重新登录
        }
        Claims claims = JwtUtil.parseJWT(token);
        if (claims == null){
            throw new BusinessException(ResultCodeEnum.LOGIN_AUTH);//提示重新登录
        }
        String userId = claims.getSubject();
        if (StrUtil.isBlank(userId)){
            throw new BusinessException(ResultCodeEnum.LOGIN_AUTH);//提示重新登录
        }
        //根据id查询用户信息
        UserProfiles userProfiles = this.baseMapper.selectById(userId);
        if (userProfiles == null){
            throw new BusinessException(ResultCodeEnum.LOGIN_AUTH);//提示重新登录
        }
        //修改密码,判断密码是否一致
        String oldPassword = userProfiles.getPassword();
        String salt = userProfiles.getSalt();
        if(salt == null && StrUtil.isBlank(oldPassword)){
            //第一次登录，需要设置密码
            //加密密码
            salt = PasswordUtil.generateSalt();
            String encrypt = PasswordUtil.encrypt(password, salt);
            userProfiles.setPassword(encrypt);
            userProfiles.setSalt(salt);
            userProfiles.setStatus(1);
            this.baseMapper.updateById(userProfiles);
            return "密码设置成功";
        }
        String encrypt = PasswordUtil.encrypt(password, salt);
        if (!encrypt.equals(oldPassword)){
            throw new BusinessException(ResultCodeEnum.PASSWORD_REPEAT_ERROR);
        }
        return "密码修改成功";
    }


    /**
     * 刷新accessToken
     * @param uuid refreshToken的key
     * @return 新的accessToken
     */
    @Override
    public UserLoginTokenVO refreshToken(String uuid) {
        if (StrUtil.isBlank(uuid)){
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        //从redis中获取refreshToken
        String refreshToken = stringRedisTemplate.opsForValue().get(REFRESH_TOKEN_KEY_PREFIX + uuid);
        if (StrUtil.isBlank(refreshToken)){
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        //验证refreshToken
        //验证refreshToken是否过期
        if (JwtUtil.isTokenExpired(refreshToken)){
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        Claims claims = JwtUtil.parseJWT(refreshToken);
        if (claims == null){
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        String userId = claims.getSubject();
        if (StrUtil.isBlank(userId)){
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }
        //生成新的accessToken
        String accessToken = JwtUtil.createJWT(userId);
        return UserLoginTokenVO.build(accessToken, uuid);
    }

    @Override
    public String updateInfo(UserProfilesUpdateDTO userProfilesVO, HttpServletRequest request) {
        String name = userProfilesVO.getName();
        Integer age = userProfilesVO.getAge();
        String occupation = userProfilesVO.getOccupation();
        String gender = userProfilesVO.getGender();
        String hobbies = userProfilesVO.getHobbies();
        String profile = userProfilesVO.getProfile();
        String loveStatus = userProfilesVO.getLoveStatus();

        if (StrUtil.isBlank(name) || age == null || StrUtil.isBlank(occupation) || StrUtil.isBlank(gender) || StrUtil.isBlank(hobbies) || StrUtil.isBlank(profile) || StrUtil.isBlank(loveStatus)){
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }

        //获取token中的用户信息
        String token = RequestHeaderUtils.getAuthorization(request);
        if (StrUtil.isBlank(token)){
            throw new BusinessException(ResultCodeEnum.LOGIN_AUTH);//提示重新登录
        }
        Claims claims = JwtUtil.parseJWT(token);
        if (claims == null){
            throw new BusinessException(ResultCodeEnum.LOGIN_AUTH);//提示重新登录
        }
        String userId = claims.getSubject();
        if (StrUtil.isBlank(userId)){
            throw new BusinessException(ResultCodeEnum.LOGIN_AUTH);//提示重新登录
        }
        //根据id查询用户信息
        UserProfiles userProfiles = this.baseMapper.selectById(userId);
        if (userProfiles == null){
            throw new BusinessException(ResultCodeEnum.LOGIN_AUTH);//提示重新登录
        }
        //修改用户信息
        userProfiles.setName(name);
        userProfiles.setAge(age);
        userProfiles.setOccupation(occupation);
        userProfiles.setGender(gender);
        userProfiles.setLoveStatus(loveStatus);
        //拼接hobbies，逗号隔开
        userProfiles.setHobbies(hobbies);
        userProfiles.setProfile(profile);
        this.baseMapper.updateById(userProfiles);
        //更新缓存
        UserProfilesVO userProfilesVO1 = UserProfilesVO.builderDBToVO(userProfiles);
        redisHutoolHashUtil.setUserProfiles(USER_INFO_KEY_PREFIX + userId,userProfilesVO1,2);//2小时过期
        return "修改成功";
    }

    /**
     * 邮箱验证
     * @param userEmailAuthDTO 邮箱验证参数
     * @return
     */
    @Override
    public ResultUtils emailAuth(UserEmailAuthDTO userEmailAuthDTO) {
        //参数校验
        if (userEmailAuthDTO == null){
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }

        String email = userEmailAuthDTO.getEmail();
        String code = userEmailAuthDTO.getCode();

        //校验参数
        if (StrUtil.isBlank(email) || StrUtil.isBlank(code)){
            throw new BusinessException(ResultCodeEnum.PARAMS_ERROR);
        }

        //邮箱是否正确
        if(!EmailRightUtils.isRightEmail(email)){
            throw new BusinessException(ResultCodeEnum.EMAIL_NOT_EXIST);
        }

        //code 要为 6 位
        if (code.length()!= 6) {
            throw new BusinessException(ResultCodeEnum.CODE_ERROR);
        }

        String rsCode = stringRedisTemplate.opsForValue().get(EMAIL_CODE_KEY_PREFIX + email);
        if (!code.equals(rsCode)){
            throw new BusinessException(ResultCodeEnum.CODE_ERROR);
        }

        String openIdUUid = userEmailAuthDTO.getUuid();
        WxInfo wxInfoByUUID = getWxInfoByUUID(openIdUUid);

        //判断邮箱认证是否存在数据库中
        //1.不在数据库中，重新入库,生成账户，并且进行输入密码和走流程
        UserProfiles user = this.lambdaQuery().select(UserProfiles::getEmail, UserProfiles::getId,UserProfiles::getUserAccount,UserProfiles::getStatus).eq(UserProfiles::getEmail, email).one();
        //可能第一次登录，需要为其创建账户，并让其设置密码

        if (user == null || StrUtil.isBlank(user.getEmail()) || StrUtil.isBlank(user.getUserAccount())){
            //创建用户
            //插入数据库
            UserProfiles userProfiles = new UserProfiles();
            userProfiles.setEmail(email);
            userProfiles.setStatus(0);
            String account = UserAccountUtils.generateUserAccount(email);
            userProfiles.setUserAccount(account);
            if (wxInfoByUUID != null){
                userProfiles.setOpenId(wxInfoByUUID.getOpenId());
                userProfiles.setAvatar(wxInfoByUUID.getHeadImgUrl());
                userProfiles.setName(wxInfoByUUID.getNickname());
            }
            baseMapper.insert(userProfiles);
            //生成accessToken 和 refreshToken
            String accessToken = JwtUtil.createJWT(String.valueOf(userProfiles.getId()));
            String refreshToken = JwtUtil.createRefreshToken(String.valueOf(userProfiles.getId()));
            //生成一个UUID作为refreshToken的key，存储到redis中
            String uuid = UUID.randomUUID().toString(false);//不携带-
            //存储refreshToken到redis (不应该用userId作为key，就用accessToken作为key)
            stringRedisTemplate.opsForValue().set(REFRESH_TOKEN_KEY_PREFIX + uuid,refreshToken,7, TimeUnit.DAYS);
            // 主动删除loginCode ，openId，uuid的映射关系
            WebSocketServiceImpl.deleteLoginCodeOpenIdUUIDCache(openIdUUid);
            return ResultUtils.okLogin(UserLoginTokenVO.buildAccount(account,accessToken,uuid));
        }


        //2.在数据库中，说明之前就有过账户，直接登录成功返回token
        //存储openId和头像
        if (wxInfoByUUID != null){
            this.baseMapper.update(new LambdaUpdateWrapper<UserProfiles>().eq(UserProfiles::getEmail,email)
                    .set(UserProfiles::getOpenId,wxInfoByUUID.getOpenId())
                    .set(UserProfiles::getAvatar,wxInfoByUUID.getHeadImgUrl())
                    .set(UserProfiles::getName,wxInfoByUUID.getNickname()));
        }
        //生成accessToken 和 refreshToken
        String accessToken = JwtUtil.createJWT(String.valueOf(user.getId()));
        String refreshToken = JwtUtil.createRefreshToken(String.valueOf(user.getId()));
        //生成一个UUID作为refreshToken的key，存储到redis中
        String uuid = UUID.randomUUID().toString(false);//不携带-
        //存储refreshToken到redis (不应该用userId作为key，就用accessToken作为key)
        stringRedisTemplate.opsForValue().set(REFRESH_TOKEN_KEY_PREFIX + uuid,refreshToken,7, TimeUnit.DAYS);
        //如果查到用户但是没认证，也就是没有自定义密码的话，需要返回一个状态并且返回特定信息
        if (0 == user.getStatus()){
            return ResultUtils.okLogin(UserLoginTokenVO.buildAccount(user.getUserAccount(),accessToken,uuid));
        }
        // 主动删除loginCode ，openId，uuid的映射关系
        WebSocketServiceImpl.deleteLoginCodeOpenIdUUIDCache(openIdUUid);
        //登录成功
        return ResultUtils.okLogin(UserLoginTokenVO.build(accessToken,uuid));
    }
}
